// Include standard headers
#include <stdio.h>
#include <stdlib.h>

// Include GLEW - OpenGL Extension Wrangler
#define GLEW_STATIC
#include <GL/glew.h>

// Include GLFW - OpenGL Framework (Multi Platform framework to Setup a window and OpenGL Context
#include <GLFW/glfw3.h>

// Include GLM - OpenGL Mathematics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
using namespace glm;

//#include <GL/glut.h> 

#include "shader.hpp"

extern "C" {
_declspec(dllexport) unsigned long NvOptimusEnablement = 0x00000001;
}

#ifndef __Common_h__
  #include "Common.h"
#endif //__Common_h__

#ifndef __Cloth_h__
  #include "Cloth.h"
#endif //__Cloth_h__

#define NUM_PARTICLES_INCR_DECR     5
#define CAMERA_POSITION_INCR_DECR   1
#define SPRINGS_ITER_INCR_DECR  1
#define WIND_SPEED_INCR             0.1f
#define DAMPING_INCR                0.01f
#define CLOTH_WIDTH                 14
#define CLOTH_LENGTH                10

enum PHYSICS_MODE {PM_CPU, PM_CPU_GPU};
enum COLOR_MODE {ECM_GLOBAL, ECM_VERTEX};

class Simulation
{

public:
	Simulation();
	~Simulation();

	bool initWindow();
	void initPhysicsProgram();
	void initRenderingsProgram();

	void updateCubePosition();
	void execPhysicsCalcs();
	void execRendering();
	void showInfo();
	void handleKeyboardEvents();

	void createCloth(int num_particles_width, int num_particles_height);

public:

	// Simulation modes/keyboard events
	GLFWwindow* window;
	PHYSICS_MODE physMode;
	COLOR_MODE colorMode;
	int cameraXPosition;
	int cameraYPosition;
	int cameraZPosition;
	int clothHangMode;
	int cameraLocation;
	int cubePos;
	int windSrcMode;
	bool bPressedB; // cube position
	bool bPressedC; // color
	bool bPressedD; // damping
	bool bPressedH; // hanging
	bool bPressedL; // One of predefined camera positions
	bool bPressedP; // GPU, CPU
	bool bPressedV; // # vertices
	bool bPressedS; // Wind Source 
	bool bPressedX;
	bool bPressedY;
	bool bPressedZ;

	// Matrices 
	glm::mat4 Projection;
	glm::mat4 View;
	glm::mat4 Model;
	glm::mat4 MVP;

	// Cloth properties
	Cloth* cloth;
	GLfloat* v3GravityForce;
	GLfloat* v3WindForce;
	GLfloat windScale;
	GLfloat v1Mass;
	GLfloat v1TimeStepSize;
	GLfloat v1Damping;
	
	// Rendering shaders params
	GLuint renderingProgramID;
	GLuint positionRendering;
	GLuint color;
	GLuint MatrixID;
	GLuint uniformColorID;
	GLuint uniformColorMode;

	// Physics shaders params
	GLuint physicsProgramID;
	GLuint idPhysics;
	GLuint positionPhysics;
	GLuint normal;
	GLuint acceleration;
	GLuint old_position;

	GLfloat* transFeedFloatArr;

	GLuint Float3vfGravityForce;
	GLuint Float1vfMass;
	GLuint Float3vfWindDirection;
	GLuint Float1vWindScale;
	GLuint Float1vfTimeStepSize;
	GLuint Float1vfDamping;

	// cube attributes
	float cube_time; // counter for the z-position of the cube
	float bounding_sphere_radius; // the radius of cube-sphere approximation
	vec3 cube_center;

	
private:
	void updateMVP();
	void drawCube(GLuint& cubeVBO);
};